let logger = require("pomelo-logger").getLogger(__filename);
let DateUtil = require("../../../utils/dateUtil");
let Random = require('../../../utils/random');
let util = require('util');
let BaseTable = require("../base/yuanbaoTable");
let pomelo = require("pomelo");
let ZhuojiGameLogicProcessor = require("../../mahjong/zhuojiGameLogicProcessor");
let ZhuojiGameConstant = require("../../mahjong/constant/zhuojiGameConstant");
let ScoreReason = require("../../mahjong/constant/scoreReason");
let MahjongUtils = require("../../mahjong/utils/MahjongUtils");
let WashCardRule = require("../../mahjong/utils/WashCardRule");
let ZhuojiMahjongProcessor = require("../../mahjong/zhuojiMahjongProcessor");
let DealRule = require("../../mahjong/utils/DealRule");
let ZhuojiNotifyActiveMsg = require("../../mahjong/msg/zhuojiNotifyActiveMsg");
let MahjongCheckResult = require("../../mahjong/domain/mahjongCheckResult");
let JiCard = require("../../mahjong/domain/jiCard");
let ScoreTuple = require("../../mahjong/domain/scoreTuple");
let RobotMsg = require("../../mahjong/robot/robotMsg");



//TODO: fee

let ZhuojiYuanBaoTable = function () {
    BaseTable.call(this);
    // this.state = ZhuojiGameConstant.TABLE_STATE_WAITING_PLAYER;
    this.playSubstate = 0;
    this.resultType = 0;    // GAME_RESULT_TYPE_LIU_JU    GAME_RESULT_TYPE_DIAN_PAO   GAME_RESULT_TYPE_ZI_MO

    this.currentPosition = -1;  //当前操作玩家
    this.waitingPlayerOperate = null;
    this.chu = {uid: -1, position: -1, chuCard: 0};
    // this.cardOpPlayerPosition = -1;         //出牌的玩家，可能会引发别的玩家的碰杠胡
    // this.currentCard = 0;
    // this.chuPlayer = null;

    this.gangOpBackup = null;               //抢杠的数据备份
    this.actorUid = -1;                     //tip指向的玩家
    this.currentHuPlayer = null;            //暂时没有用
    this.lastAction = 0;                    //上一次操作
    this.dealerUid = 0;

    this.cards = [];                //底牌
    this.cardPool = new Map();      //牌池


    this.readyTime = 0;             //所有玩家都准备的时间
    this.handStartTime = 0;         //牌局开始时间
    this.handEndTime = 0;           //牌局结束时间
    // this.actorTime = 0;             //当前操作玩家计时
    this.lastEnterTime = 0;         //上一个玩家进入桌子时间

    this.zhuojiGameLogicProcessor = null;

    /**鸡牌*/
    this.jiCard = 0;                //牌局结束后所翻的鸡牌
    this.jiPositions = {chongFeng: -1, zeRen: -1, chongFengWugu: -1, zeRenWugu: -1};

    /**玩法*/
    this.fanpaiType = 1;    //1.翻牌鸡，2.摇摆鸡
    this.benji = false;
    this.wuguji = true;
    this.xingqiji = false;
    this.chuifengji = true;
    this.playType = 1;      //1.连庄，2.样样三

    /**结果*/
    this.playerGameResults = new Map();

    this.playerHuTypes = new Map();
    this.playerHuExtraTypes = new Map();
    this.playerFans = new Map();

    this.enabledPlayers = [];
    this.disabledPlayers = [];

    this.playerJiCards = new Map();
    this.playerGangFans = new Map();
    this.playerJiFans = new Map();
    this.playerHuFans = new Map();
    this.playerTotalScore = new Map();

    this.scoreTuples = [];

};

util.inherits(ZhuojiYuanBaoTable, BaseTable);

ZhuojiYuanBaoTable.prototype.onAddPlayer = function (player) {
    logger.info(`---------------${JSON.stringify(this.toJSON())}`);
    player.push("onInitializeZhuojiTable", {
        table: this.toJSON(),
        myCards: player.cardsInHand
    });
    this.broadCastWithout("onAddOrUpdateZhuojiPlayer", {
        player : player.toJSON()
    }, player.getUid());
};

ZhuojiYuanBaoTable.prototype.getState = function () {
    return this.state;
};

ZhuojiYuanBaoTable.prototype.setState = function (state) {
    this.state = state;
};

ZhuojiYuanBaoTable.prototype.getPlaySubstate = function () {
    return this.playSubstate;
};

ZhuojiYuanBaoTable.prototype.setPlaySubstate = function (playSubstate) {
    this.playSubstate = playSubstate;
};

ZhuojiYuanBaoTable.prototype.getWaitingPlayerOperate = function () {
    return this.waitingPlayerOperate;
};

ZhuojiYuanBaoTable.prototype.setWaitingPlayerOperate = function (waitingPlayerOperate) {
    this.waitingPlayerOperate = waitingPlayerOperate;
};

ZhuojiYuanBaoTable.prototype.getReadyTime = function () {
    return this.readyTime;
};

ZhuojiYuanBaoTable.prototype.setReadyTime = function (readyTime) {
    this.readyTime = readyTime;
};

ZhuojiYuanBaoTable.prototype.getHandEndTime = function () {
    return this.handEndTime;
};

ZhuojiYuanBaoTable.prototype.setHandEndTime = function (handEndTime) {
    this.handEndTime = handEndTime;
};

ZhuojiYuanBaoTable.prototype.getHandStartTime = function () {
    return this.handStartTime;
};

ZhuojiYuanBaoTable.prototype.setHandStartTime = function (handStartTime) {
    this.handStartTime = handStartTime;
};

ZhuojiYuanBaoTable.prototype.getLastEnterTime = function () {
    return this.lastEnterTime;
};

ZhuojiYuanBaoTable.prototype.setLastEnterTime = function (lastEnterTime) {
    this.lastEnterTime = lastEnterTime;
};

ZhuojiYuanBaoTable.prototype.getCurrentPosition = function () {
    return this.currentPosition;
};

ZhuojiYuanBaoTable.prototype.setCurrentPosition = function (currentPosition) {
    this.currentPosition = currentPosition;
};

ZhuojiYuanBaoTable.prototype.getChu = function () {
    return this.chu;
};

ZhuojiYuanBaoTable.prototype.setChu = function (uid, position, card) {
    this.chu.uid = uid;
    this.chu.position = position;
    this.chu.card = card;
};

ZhuojiYuanBaoTable.prototype.clearChu = function () {
    this.chu.uid = -1;
    this.chu.position = -1;
    this.chu.card = 0;
};

ZhuojiYuanBaoTable.prototype.getCardOpPlayerPosition = function () {
    // return this.cardOpPlayerPosition;
    return this.chu.position
};

ZhuojiYuanBaoTable.prototype.setCardOpPlayerPosition = function (position) {
    // this.cardOpPlayerPosition = cardOpPlayerPosition;
    this.chu.position = position;
};

ZhuojiYuanBaoTable.prototype.getCurrentCard = function () {
    return this.chu.card;
};

ZhuojiYuanBaoTable.prototype.setCurrentCard = function (card) {
    this.chu.card = card;
};

ZhuojiYuanBaoTable.prototype.getChuPlayer = function () {
    return this.chuPlayer;
};

ZhuojiYuanBaoTable.prototype.setChuPlayer = function (chuPlayer) {
    this.chuPlayer = chuPlayer;
};

ZhuojiYuanBaoTable.prototype.getCurrentHuPlayer = function () {
    return this.currentHuPlayer;
};

ZhuojiYuanBaoTable.prototype.setCurrentHuPlayer = function (currentHuPlayer) {
    this.currentHuPlayer = currentHuPlayer;
};

ZhuojiYuanBaoTable.prototype.getGangOpBackup = function () {
    return this.gangOpBackup;
};

ZhuojiYuanBaoTable.prototype.setGangOpBackup = function (gangOpBackup) {
    this.gangOpBackup = gangOpBackup;
};

ZhuojiYuanBaoTable.prototype.setLastAction = function (lastAction) {
    this.lastAction = lastAction;
};

ZhuojiYuanBaoTable.prototype.getLastAction = function () {
    return this.lastAction;
};

ZhuojiYuanBaoTable.prototype.setLastChuAction = function () {
    if (this.lastAction === ZhuojiGameConstant.ACTION_MING_GANG) {
        this.lastAction = ZhuojiGameConstant.ACTION_MING_GANG_CHU;
    } else if (this.lastAction == ZhuojiGameConstant.ACTION_AN_GANG) {
        this.lastAction = ZhuojiGameConstant.ACTION_AN_GANG_CHU;
    } else if (this.lastAction == ZhuojiGameConstant.ACTION_BU_GANG) {
        this.lastAction = ZhuojiGameConstant.ACTION_BU_GANG_CHU;
    } else {
        this.lastAction = ZhuojiGameConstant.ACTION_CHU;
    }
};

ZhuojiYuanBaoTable.prototype.setLastPengAction = function () {
    this.lastAction = ZhuojiGameConstant.ACTION_PENG;
};

ZhuojiYuanBaoTable.prototype.getActionType =function () {
    if(this.lastAction == ZhuojiGameConstant.ACTION_CHU){
        return 1;
    } else if (this.lastAction == ZhuojiGameConstant.ACTION_AN_GANG || this.lastAction == ZhuojiGameConstant.ACTION_AN_GANG_CHU){
        return 2;
    } else if (this.lastAction == ZhuojiGameConstant.ACTION_MING_GANG || this.lastAction == ZhuojiGameConstant.ACTION_MING_GANG_CHU){
        return 3;
    } else if (this.lastAction == ZhuojiGameConstant.ACTION_BU_GANG_CHU){
        return 4;
    } else if (this.lastAction == ZhuojiGameConstant.ACTION_BU_GANG){
        return 5;
    } else {
        return 1;
    }

};

ZhuojiYuanBaoTable.prototype.getDealerPos = function () {
    for(let player of this.players) {
        if(player.getUid() === this.dealerUid) {
            return player.getPosition();
        }
    }
    return -1;
};

ZhuojiYuanBaoTable.prototype.getDealerUid = function () {
    return this.dealerUid;
};

ZhuojiYuanBaoTable.prototype.setDealerUid = function (dealerUid) {
    this.dealerUid = dealerUid;
};

ZhuojiYuanBaoTable.prototype.getCards = function () {
    return this.cards;
};

ZhuojiYuanBaoTable.prototype.setCards = function (cards) {
    this.cards = [...cards];
};


ZhuojiYuanBaoTable.prototype.setReady = function (player) {
    if (this.playingFlag >= 1) {
    // if (this.state === ZhuojiGameConstant.TABLE_STATE_PLAYING) {
        logger.error("game has been started");
        return false;
    }
    if (player.getBalance() < this.getFee()) {
        return false;
    }
    if (player.getBalance() < this.room.minStack || player.getBalance() > (this.room.maxStack || Number.MAX_VALUE )) {
        return false;
    }
    player.setReady();
    let now = DateUtil.nowMS();
    //TODO ensure
    // this.lastReadyPlayerTime = now;
    if(this.isAllReady()) {
        this.setReadyTime(now);
    }
    return true;
};

ZhuojiYuanBaoTable.prototype.tuoGuan = function (player, auto) {
    player.autoOperation = auto;
    this.broadCast("onZhuojiTuoGuan", {uid : player.getUid(), auto : auto});
};

ZhuojiYuanBaoTable.prototype.addCardToCardPool = function (card) {
    if (this.cardPool.has(card)) {
        this.cardPool.set(card, this.cardPool.get(card) + 1);
    } else {
        this.cardPool.set(card, 1);
    }
};

ZhuojiYuanBaoTable.prototype.addCardsToCardPool = function (cards) {
    for (let card of cards) {
        if(this.cardPool.has(card)) {
            this.cardPool.set(card, this.cardPool.get(card) + 1);
        }else{
            this.cardPool.set(card, 1);
        }
    }
};


ZhuojiYuanBaoTable.prototype.getPlayerByTablePos = function (tablePos) {
    if(tablePos >= this.players.length || tablePos < 0){
        return null;
    }
    for(let player of this.players) {
        if(player != null && player.getPosition() === tablePos) {
            return player;
        }
    }
    return null;
};

ZhuojiYuanBaoTable.prototype.getPlayerByUid = function (uid) {
    for(let player of this.players) {
        if(player != null && player.getUid() === uid) {
            return player;
        }
    }
    return null;
};

ZhuojiYuanBaoTable.prototype.getCurrentOperationPlayer = function () {
    return this.currentPosition >= 0 && this.currentPosition < this.players.length
        ?
        this.players[this.currentPosition]
        :
        null;
};

ZhuojiYuanBaoTable.prototype.moveToNextPlayer = function () {
    this.currentPosition = (this.currentPosition + 1) % this.players.length;
};

ZhuojiYuanBaoTable.prototype.isEnd = function () {
    return this.getCardLeftNum() <= 0;
};

ZhuojiYuanBaoTable.prototype.getCardLeftNum = function () {
    return null == this.cards ? 0 : this.cards.length;
};

ZhuojiYuanBaoTable.prototype.popFirstCard = function () {
    return this.cards.length > 0 ? this.cards.shift() : 0;
};

ZhuojiYuanBaoTable.prototype.popLastCard = function () {
    return this.cards.length > 0 ? this.cards.pop() : 0;
};

ZhuojiYuanBaoTable.prototype.getFirstCard = function () {
    return this.cards.length > 0 ? this.cards[0] : 0;
};
ZhuojiYuanBaoTable.prototype.getLastCard = function () {
    return this.cards.length > 0 ? this.cards[this.cards.length - 1] : 0;
};


/**设置翻牌鸡**/
ZhuojiYuanBaoTable.prototype.setFanpaiJiCard = function () {
    this.jiCard = this.getFirstCard();
};

ZhuojiYuanBaoTable.prototype.getFanPaiJiCard = function () {
    return this.jiCard;
};

ZhuojiYuanBaoTable.prototype.isChongfeng = function (card) {
    return card === 0x11 && this.cardPool.get(card) === 1;
};

ZhuojiYuanBaoTable.prototype.isChongfengWugu = function (card) {
    return card === 0x28 && this.cardPool.get(card) === 1;
};

ZhuojiYuanBaoTable.prototype.isZeRen = function (card, operation) {
    if (operation === ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) {
        return card === 0x11 && this.cardPool.get(card) === 3;
    } else if (operation === ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) {
        return card === 0x11 && this.cardPool.get(card) === 4;
    } else {
        logger.error(`isZeRen unexpected operation 0x${operation.toString(16)}`);
        return false;
    }
};

ZhuojiYuanBaoTable.prototype.isZeRenWugu = function (card, operation) {
    if(operation === ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) {
        return card == 0x28 && this.cardPool.get(card) === 3;
    } else if(operation === ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) {
        return card == 0x28 && this.cardPool.get(card) === 4;
    } else {
        logger.error(`isZeRen unexpected operation 0x${operation.toString(16)}`);
        return false;
    }
};

ZhuojiYuanBaoTable.prototype.join = function (player) {
    for (let i = 0; i < this.players.length; i++) {
        if (this.players[i] == null) {
            this.players[i] = player;
            player.position = i;
            player.resetInTable();
            break;
        }
    }
    this.lastEnterTime = DateUtil.nowMS();
    this.onAddPlayer(player);
    if (!this.getIsPlaying()) {
        player.startGetReadyCountDown(16);
    }
};


/**
 *提示玩家出牌
 */
ZhuojiYuanBaoTable.prototype.notifyActivePlayer = function (canHu) {
    let player = this.getCurrentOperationPlayer();
    if (player == null) {
        logger.error(`---notifyActivePlayer--- invalid table. activePlayer is null. tableId=${this.getTid()}`);
        return;
    }
    logger.info(`---notifyActivePlayer start--- playerUid:${player.getUid()}`);

    this.actorUid = player.getUid();
    let grabCard = player.cardGrab;
    let hasGrabCard = grabCard === 0 ? 0 : 1;
    this.broadCast("onZhuojiTip", {uid : player.getUid(), hasGrabCard : hasGrabCard, leftNum : this.getCardLeftNum()});

    let msg = new ZhuojiNotifyActiveMsg();
    msg.uid = player.getUid();
    msg.operation = ZhuojiGameConstant.MAHJONG_OPERTAION_CHU;
    msg.grabCard = grabCard;

    //check win
    // let huType = player.calculateHuType(grabCard);
    // let extraHuType = player.tingChuType;

    let winResult = null;
    if (canHu) {
        winResult = ZhuojiMahjongProcessor.checkWin(grabCard, player, this);
    }
    if (winResult != null) {
        msg.operation = msg.operation | ZhuojiGameConstant.MAHJONG_OPERTAION_HU;
    }

    //check gang
    let gangResult = ZhuojiMahjongProcessor.checkGang(this, grabCard, player, true, player);
    player.gangList = gangResult == null ? [] : gangResult.gangList;
    if (gangResult != null) {
        msg.operation = msg.operation | gangResult.operation;
        msg.gangList = [...gangResult.gangList];
    }

    //check ting
    if (!player.onclickTing && player.chuPaiNum === 0 && player.cardsDown.length === 0) {
        let tingResult = ZhuojiMahjongProcessor.checkTing(player, grabCard);
        if (tingResult != null) {
            msg.operation = msg.operation | ZhuojiGameConstant.MAHJONG_OPERTAION_TING;
            msg.tingList = [...tingResult.tingList];
        }
    }
    msg.tingResults = this.getTing14Hint(player, grabCard);

    let checkResult = new MahjongCheckResult();
    checkResult.uid = player.getUid();
    checkResult.operation = msg.operation;
    checkResult.targetCard = grabCard;
    if (gangResult != null) {
        checkResult.gangList = gangResult.gangList;
    }

    this.setWaitingPlayerOperate(checkResult);

    if (player.onclickTingChuPai && winResult == null) {
        this.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_AUTO_MO_CARD);
        return;
    }
    player.push("onZhuojiNotifyActive", msg.toJSON());
    player.opStartTime = DateUtil.nowMS();
    if (player.isRobot === 1) {
        pomelo.app.get("zhuojiRobotService").pushMsg(new RobotMsg(this, player, msg.toJSON(), DateUtil.nowMS()));
    }
    this.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_IDLE);
    logger.info(`---notifyActivePlayer end--- notifymsg operation:0x${msg.operation.toString(16)} player.isRobot : ${player.isRobot}`);
};

ZhuojiYuanBaoTable.prototype.getTing14Hint = function(player, grabCard) {
    let playerAllCards = [...player.cardsInHand];
    if(grabCard != 0){
        playerAllCards.push(grabCard);
    }
    let tingList = MahjongUtils.checkTing14(playerAllCards);

    let results = [];
    for(let [card, tingCards] of tingList.entries()){
        let tingCardsNums = [];
        for(let tingCard of tingCards){
            tingCardsNums.push({
                "tingCard" : tingCard,
                "count" : this.getCardAvailableCount(player, tingCard)
            });
        }
        results.push({
            "card" : card,
            "tingCardsNums" : tingCardsNums
        });
    }
    return results;
};


ZhuojiYuanBaoTable.prototype.getTing13Hint = function(player) {
    let huList = MahjongUtils.checkTing13([...player.cardsInHand]);
    let results = [];
    for(let card of huList){
        results.push({
            "tingCard" : card,
            "count" : this.getCardAvailableCount(player, card)
        });
    }
    return results;
};

ZhuojiYuanBaoTable.prototype.getCardAvailableCount = function(pl, card) {
    logger.info(`------getCardAvailableCount----`);
    let count = 0;
    for (let player of this.getAllPlayers()) {
        count += player.countCardInBefore(card);
        count += player.countCardInDown(card);
        if (pl.getUid() === player.getUid()) {
            count += player.countCardInHand(card);
        }
    }
    if(this.chu.card !== 0 && this.chu.card === card){
        count ++;
    }
    logger.info(`------getCardAvailableCount---- ${count}`);
    count = 4 - count;
    if (count < 0) {
        logger.error(`invalid card availableCount. card=${card}`);
        count = 0;
    }
    return count;
};



ZhuojiYuanBaoTable.prototype.isReady = function(){
    if(!this.isFull()) {
        return false;
    }
    for(let player of this.players) {
        if(!player.isReady()) {
            return false;
        }
    }
    return true;
};

ZhuojiYuanBaoTable.prototype.gameStart = function () {
    //扣钱
    let fee = this.getFee();
    let params = {uids: [], amounts:[], params:[]};
    for (let player of this.players) {
        if (player.isRobot === 0) {
            params.uids.push(player.getUid());
            params.amounts.push(-fee);
            params.params.push("zhuoji_start");
            logger.info(`tableId=${this.tid}, player=${player.getUid()}, fee=${-fee}`);
        }
    }
    pomelo.app.rpc.account.tableRemote.changeGold(null, params, function (){});

    for (let player of this.players) {
        player.changeBalance(-fee);
        this.broadCast("onPlayerBalanceChange", {
            uid: player.getUid(),
            balance: player.getBalance()
        });
    }

    for (let player of this.getAllPlayers()) {
        player.clearGetReadyCountDown();
    }
    this.setHandStartTime(DateUtil.nowMS());
    this.setHandEndTime(0);

    let dealerPosition = Random.randRange(0, this.getNumOfPlayers() - 1);
    let dealer = this.getPlayerByTablePos(dealerPosition);
    this.setDealerUid(dealer.getUid());
    //TODO
    // this.resetPosition();// 把玩家按座位排序
    this.cards = WashCardRule.washCards();  // 洗牌
    // this.cards = WashCardRule.doZuopai();   // 配牌
    DealRule.dealCard(this);                // 发牌
    logger.info(`----------gameStart----------- ${this.cards}`);

    // this.setCurrentCard(0);
    // this.setCardOpPlayerPosition(-1);
    this.currentPosition = dealerPosition;

    logger.debug(`dealerPos:${dealerPosition} dealerUid ${this.dealerUid}`);

    for(let player of this.getAllPlayers()) {
        player.push("onZhuojiStart", {"dealerUid": this.dealerUid, "myCards": player.cardsInHand});
    }

    this.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_WAITING_CLIENT_SHOW_INIT_CARDS);
    // this.setState(ZhuojiGameConstant.TABLE_STATE_PLAYING);
    this.playingFlag = 1;
};

ZhuojiYuanBaoTable.prototype.gameFinish = function (results) {
    logger.debug(`table: ${this.tid} gameFinish start`);

    //扣钱
    let params = {uids: [], amounts:[], params:[]};
    for (let result of results) {
        let player = this.getPlayerByUid(result.uid);
        if (player.isRobot === 0) {
            params.uids.push(result.uid);
            params.amounts.push(result.score);
            params.params.push("zhuoji_finish");
            logger.info(`tableId=${this.tid}, player=${result.uid}, score=${result.score}`);
        }
    }
    pomelo.app.rpc.account.tableRemote.changeGold(null, params, function (){});

    for (let result of results) {
        let player = this.getPlayerByUid(result.uid);
        player.changeBalance(result.score);
        this.broadCast("onPlayerBalanceChange", {
            uid: player.getUid(),
            balance: player.getBalance()
        });
    }

    //战绩
    let playerScores = [];
    for (let result of results) {
        playerScores.push({uid : result.uid, nickname: result.nickname, score : result.score});
    }
    let gameRecord = {
        gameId: this.gid,
        tableId: this.tid,
        time: DateUtil.nowMS(),
        playerScores: playerScores
    };
    pomelo.app.rpc.account.gameRecordRemote.addGameRecord(null, gameRecord, function(response) {
        if (response.resultCode != 0) {
            logger.info(`zhuojiYuanBaoTable addGameRecord failed`);
            return;
        }
        logger.info(`zhuojiYuanBaoTable addGameRecord ${JSON.stringify(response.gameRecord)}`);
    });

    //踢出机器人
    for (let player of this.getAllPlayers()) {
        if (player.isRobot === 1) {
            setTimeout( () => pomelo.app.get("zhuojiRobotService").kickRobot(this, player), Random.randRange(5000, 9000));
        }
    }

    this.reset();
    logger.debug(`tableId: ${this.tid} gameFinish end`);
};


ZhuojiYuanBaoTable.prototype.resetPosition = function () {
    this.players.sort((player1, player2) => player1.getPosition() - player2.getPosition());
};


ZhuojiYuanBaoTable.prototype.gameSettlement = function (huPlayers, lastActor, card, actionType) {
    this.playerGameResults.clear();
    this.playerHuTypes.clear();
    this.playerHuExtraTypes.clear();
    this.playerHuFans.clear();
    this.playerFans.clear();
    this.enabledPlayers = [];
    this.disabledPlayers= [];
    this.playerGangFans.clear();
    this.playerJiFans.clear();
    this.playerJiCards.clear();
    this.playerTotalScore.clear();
    this.scoreTuples = [];

    //流局     查叫的玩家要给听牌的玩家对应的胡番，鸡，豆都不算分
    if (huPlayers.length === 0) {
        this.resultType = ZhuojiGameConstant.GAME_RESULT_TYPE_LIU_JU;
        for (let player of this.getAllPlayers()) {
            let tingFan = MahjongUtils.calculateTingFan([...player.cardsInHand], [...player.cardsDown], this.playType);
            this.playerFans.set(player, tingFan);
            let result = tingFan > 0 ? ZhuojiGameConstant.PLAYER_RESULT_TYPE_TING : ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO;
            this.playerGameResults.set(player, result);
            this.playerHuTypes.set(player, 0);
            this.playerHuExtraTypes.set(player, []);
            this.playerHuFans.set(player, 0);
            this.playerGangFans.set(player, 0);
            this.playerJiCards.set(player, []);
            this.playerJiFans.set(player, 0);
        }
        for (let [player, fan] of this.playerFans.entries()) {
            if (fan > 0) {
                for (let [otherPlayer, otherFan] of this.playerFans.entries()) {
                    let result = this.playerGameResults.get(otherPlayer);
                    if (result === ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO) {
                        this.playerHuFans.set(player, this.playerHuFans.get(player) + fan);
                        this.playerHuFans.set(otherPlayer, this.playerHuFans.get(otherPlayer) - fan);
                        this.scoreTuples.push(new ScoreTuple(player, otherPlayer, fan, ScoreReason.HU));
                    }
                }
            }
        }
        for (let player of this.getAllPlayers()) {
            this.playerGangFans.set(player, 0);
            this.playerJiCards.set(player, []);
            this.playerJiFans.set(player, 0);
        }
    }
    //自摸
    else if (huPlayers.length === 1 && huPlayers[0].getUid() === lastActor.getUid()) {
        this.resultType = ZhuojiGameConstant.GAME_RESULT_TYPE_ZI_MO;
        for (let player of this.getAllPlayers()) {
            let huType = 0;
            if (player.getUid() === lastActor.getUid()) {
                huType = player.calculateHuType(card);
                this.playerGameResults.set(player, ZhuojiGameConstant.PLAYER_RESULT_TYPE_ZI_MO);
                this.playerHuTypes.set(player, huType);
                let extraHuType = 0;
                if (actionType === 1 && player.tingChuType === 1) {
                    extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_TING_YING_BAO;
                } else if (actionType === 1 && player.tingChuType === 2) {
                    extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_TING_RUAN_BAO;
                } else if (actionType === 2) {
                    extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_AN_GANG_MO;
                } else if (actionType === 3) {
                    extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_MING_GANG_MO;
                } else if (actionType === 4) {
                    extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_BU_GANG_MO;
                }
                let extraHuTypes = extraHuType === 0 ? [] : [extraHuType];

                this.playerFans.set(player, MahjongUtils.getHuTypeFan(huType, this.playType) + MahjongUtils.getHuExtraTypeFan(extraHuType));
                this.playerHuFans.set(player, 0);
                this.playerHuExtraTypes.set(player, extraHuTypes);
            } else if (MahjongUtils.isTing([...player.cardsInHand])) {
                this.playerGameResults.set(player, ZhuojiGameConstant.PLAYER_RESULT_TYPE_TING);
                this.playerHuTypes.set(player, 0);
                this.playerHuExtraTypes.set(player, []);
                this.playerFans.set(player, 0);
                this.playerHuFans.set(player, 0);
            } else {
                this.playerGameResults.set(player, ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO);
                this.playerHuTypes.set(player, 0);
                this.playerHuExtraTypes.set(player, []);
                this.playerFans.set(player, 0);
                this.playerHuFans.set(player, 0);
            }
        }
        for (let [player, fan] of this.playerFans.entries()) {
            if (fan > 0) {
                for (let otherPlayer of this.playerFans.keys()) {
                    if (player.getUid() !== otherPlayer.getUid()) {
                        this.playerHuFans.set(player, this.playerHuFans.get(player) + fan);
                        this.playerHuFans.set(otherPlayer, this.playerHuFans.get(otherPlayer) - fan);
                        this.scoreTuples.push(new ScoreTuple(player, otherPlayer, fan, ScoreReason.HU));
                    }
                }
            }
        }
        //TODO
        // calculateLianzhuangScoreWhileZimo(lastActor);
        for (let player of this.playerGameResults.keys()) {
            let result = this.playerGameResults.get(player);
            if (result !== ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO) {
                this.enabledPlayers.push(player);
            } else {
                this.disabledPlayers.push(player);
            }
        }
        this.playerGangFans = this.calculatePlayerGangFans(this.enabledPlayers);
        this.playerJiCards = this.calculateEnableJiCards(this.enabledPlayers);
        this.playerJiFans = this.calculateJiScore(this.enabledPlayers, this.playerJiCards, card);
        let disableJiCards = this.calculateDisableJiCards(this.disabledPlayers);
        for(let [key, value] of disableJiCards.entries()) {
            this.playerJiCards.set(key, value);
        }
        this.calculateJiLoseForNotTing(this.disabledPlayers, this.enabledPlayers, this.playerGameResults, disableJiCards, this.playerJiFans);
        this.playerJiCards = filterEmptyJiCardWithoutChuifengji(this.playerJiCards);
    }
    //点炮, 或者抢杠
    else {
        this.resultType = ZhuojiGameConstant.GAME_RESULT_TYPE_DIAN_PAO;
        for (let player of this.getAllPlayers()) {
            let isHuPlayer = false;
            for (let huPlayer of huPlayers) {
                if (huPlayer.getUid() === player.getUid()) {
                    isHuPlayer = true;
                    break;
                }
            }
            if (isHuPlayer) {
                this.playerGameResults.set(player, ZhuojiGameConstant.PLAYER_RESULT_TYPE_HU);
                let huType = player.calculateHuType(card);
                let fan = 0;
                this.playerHuTypes.set(player, huType);
                fan = MahjongUtils.getHuTypeFan(huType, this.playType);
                if (actionType === 5) {
                    //如果是抢杠胡，那么要翻倍
                    fan = fan * (this.getAllPlayers().length - 1);
                }
                let extraHuTypes = [];
                if (player.tingChuType === 1) {
                    extraHuTypes.push(ZhuojiGameConstant.MAHJONG_HU_CODE_TING_YING_BAO);
                    fan += MahjongUtils.getHuExtraTypeFan(ZhuojiGameConstant.MAHJONG_HU_CODE_TING_YING_BAO);
                }
                if (player.tingChuType === 2) {
                    extraHuTypes.push(ZhuojiGameConstant.MAHJONG_HU_CODE_TING_RUAN_BAO);
                    fan += MahjongUtils.getHuExtraTypeFan(ZhuojiGameConstant.MAHJONG_HU_CODE_TING_RUAN_BAO);
                }
                if (lastActor.tingChuType === 1 || lastActor.tingChuType === 2) {
                    extraHuTypes.push(ZhuojiGameConstant.MAHJONG_HU_CODE_SHA_BAO);
                    fan += MahjongUtils.getHuExtraTypeFan(ZhuojiGameConstant.MAHJONG_HU_CODE_SHA_BAO);
                }
                if (actionType == 2) {
                    extraHuTypes.push(ZhuojiGameConstant.MAHJONG_HU_CODE_AN_GANG_PAO);
                    fan += MahjongUtils.getHuExtraTypeFan(ZhuojiGameConstant.MAHJONG_HU_CODE_AN_GANG_PAO);
                }
                if (actionType == 3) {
                    extraHuTypes.push(ZhuojiGameConstant.MAHJONG_HU_CODE_MING_GANG_PAO);
                    fan += MahjongUtils.getHuExtraTypeFan(ZhuojiGameConstant.MAHJONG_HU_CODE_MING_GANG_PAO);
                }
                if ( actionType == 4) {
                    extraHuTypes.push(ZhuojiGameConstant.MAHJONG_HU_CODE_BU_GANG_PAO);
                    fan += MahjongUtils.getHuExtraTypeFan(ZhuojiGameConstant.MAHJONG_HU_CODE_BU_GANG_PAO);
                }
                if (actionType == 5) {
                    extraHuTypes.push(ZhuojiGameConstant.MAHJONG_HU_CODE_QIANG_GANG_HU);
                    fan += MahjongUtils.getHuExtraTypeFan(ZhuojiGameConstant.MAHJONG_HU_CODE_QIANG_GANG_HU);
                }

                this.playerHuExtraTypes.set(player, extraHuTypes);
                this.playerFans.set(player, fan);
                this.playerHuFans.set(player, 0);
            } else if (MahjongUtils.isTing([...player.cardsInHand])) {
                this.playerGameResults.set(player, ZhuojiGameConstant.PLAYER_RESULT_TYPE_TING);
                this.playerHuTypes.set(player, 0);
                this.playerHuExtraTypes.set(player, []);
                this.playerFans.set(player, 0);
                this.playerHuFans.set(player, 0);
            } else {
                this.playerGameResults.set(player, ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO);
                this.playerHuTypes.set(player, 0);
                this.playerHuExtraTypes.set(player, []);
                this.playerFans.set(player, 0);
                this.playerHuFans.set(player, 0);
            }
        }
        for (let player of huPlayers) {
            let fan = this.playerFans.get(player);
            this.playerHuFans.set(player, this.playerHuFans.get(player) + fan);
            this.playerHuFans.set(lastActor, this.playerHuFans.get(lastActor) - fan);
            this.scoreTuples.push(new ScoreTuple(player, lastActor, fan, ScoreReason.HU));
        }
        //TODO
        // calculateLianzhuangScoreWhileDianpao(huPlayers, lastActor);

        for (let [player, result] of this.playerGameResults.entries()) {
            //玩家被抢杠或者热炮且听牌，那么鸡和豆都不算
            if (actionType !== 1 && lastActor.getUid() === player.getUid() &&
                result !== ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO) {
                continue;
            }
            if (result !== ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO) {
                this.enabledPlayers.push(player);
            } else {
                this.disabledPlayers.push(player);
            }
        }
        this.playerGangFans = this.calculatePlayerGangFans(this.enabledPlayers);
        this.playerJiCards = this.calculateEnableJiCards(this.enabledPlayers);
        this.playerJiFans = this.calculateJiScore(this.enabledPlayers, this.playerJiCards, card);
        let disableJiCards = this.calculateDisableJiCards(this.disabledPlayers);
        for(let [key, value] of disableJiCards) {
            this.playerJiCards.set(key, value);
        }
        this.calculateJiLoseForNotTing(this.disabledPlayers, this.enabledPlayers, this.playerGameResults, disableJiCards, this.playerJiFans);
        this.playerJiCards = filterEmptyJiCardWithoutChuifengji(this.playerJiCards);
    }
    this.playerTotalScore = calculateTotalScore(this.playerHuFans, this.playerGangFans, this.playerJiFans);



    //结算
    let coinResults = this.calculateCoinResult();
    let huWinner = null;
    let totalWinner = null;
    let bestHuFan = 0;
    let bestTotalFan = 0;
    for (let player of this.getAllPlayers()) {
        let huFan = MahjongUtils.getHuTypeFan(this.playerHuTypes.get(player), this.playType);
        //除掉平胡
        if (huFan > bestHuFan && huFan > 3) {
            huWinner = player;
            bestHuFan = huFan;
        }
        let totalfan = coinResults.get(player);
        if (totalfan > bestTotalFan) {
            totalWinner = player;
            bestTotalFan = totalfan;
        }
    }
    let results = [];
    for (let player of this.getAllPlayers()) {
        let zhuojiResult = {};
        let huResult = this.playerGameResults.get(player);
        zhuojiResult.uid = player.getUid();
        zhuojiResult.nickname = player.getNickname();
        zhuojiResult.avatar = player.getAvatar();
        zhuojiResult.result = huResult;

        let jiCardJSON = [];
        let jiCards = this.playerJiCards.get(player);
        if(jiCards != null) {
            for (let jiCard of jiCards) {
                jiCardJSON.push({type : jiCard.type, value : jiCard.card, count : jiCard.count});
            }
        }
        zhuojiResult.jiCards = jiCardJSON;
        zhuojiResult.score = coinResults.get(player);

        zhuojiResult.totalFan = this.playerHuFans.get(player) + this.playerJiFans.get(player) + this.playerGangFans.get(player);
        zhuojiResult.jiFan = this.playerJiFans.get(player);
        zhuojiResult.gangFan = this.playerGangFans.get(player);

        let huType = this.playerHuTypes.get(player);
        if (huType !== 0) {
            zhuojiResult.hu = {
                name : MahjongUtils.getHuTypeEnName(huType),
                fan : MahjongUtils.getHuTypeFan(huType, this.playType),
                // type : MahjongUtils.getClienHuType(huType),
                type : huType,
                cnName : MahjongUtils.getHuTypeName(huType)
            };
        }

        let extraHuTypes = this.playerHuExtraTypes.get(player);
        if (extraHuTypes.length > 0) {
            let extraHuTypesJSON = [];
            for (let extraHutype of extraHuTypes) {
                extraHuTypesJSON.push({
                    name : MahjongUtils.getHuExtraTypeEnName(extraHutype),
                    type : extraHutype,
                    fan : MahjongUtils.getHuExtraTypeFan(extraHutype),
                });
            }
            zhuojiResult.extraHu = extraHuTypesJSON;
        }

        if (huWinner != null && huWinner.getUid() === player.getUid()) {
            zhuojiResult.huWinner = 1;
        }
        if (totalWinner != null && totalWinner.getUid() === player.getUid()) {
            zhuojiResult.totalWinner = 1;
        }
        zhuojiResult.cardsInHand = player.cardsInHand;
        zhuojiResult.cardsDown = player.cardsDown;
        logger.debug(`zhuojiResult : ${JSON.stringify(zhuojiResult)}`);
        results.push(zhuojiResult);
    }
    return results;

};

ZhuojiYuanBaoTable.prototype.calculateCoinResult = function() {
    let scoreMatrix = new Map();
    //初始化分数矩阵
    for (let player of this.getAllPlayers()) {
        let scoreVector = new Map();
        scoreMatrix.set(player, scoreVector);
        for (let otherPlayer of this.getAllPlayers()) {
            scoreVector.set(otherPlayer, 0);
        }
    }
    //设置分数矩阵
    for (let tuple of this.scoreTuples) {
        let score1 = scoreMatrix.get(tuple.winner).get(tuple.loser);
        score1 = score1 + tuple.score * this.multiplier;
        let score2 = scoreMatrix.get(tuple.loser).get(tuple.winner);
        score2 = score2 - tuple.score * this.multiplier;
        scoreMatrix.get(tuple.winner).set(tuple.loser, score1);
        scoreMatrix.get(tuple.loser).set(tuple.winner, score2);
    }
    //玩家赢的钱不能超过自身携带的金币
    for (let player of this.getAllPlayers()) {
        let scoreVector = scoreMatrix.get(player);
        for (let p of scoreVector.keys()) {
            let maxScore = player.getBalance();
            if (maxScore < scoreVector.get(p)) {
                scoreVector.set(p, maxScore);
                scoreMatrix.get(p).set(player, -maxScore);
            }
        }
    }
    //玩家输的钱不能超过玩家本身的携带
    for (let player of this.getAllPlayers()) {
        let positiveScore = player.getBalance();
        let negtiveScore  = 0;
        let scoreVector = scoreMatrix.get(player);
        for (let p of scoreVector.keys()) {
            let score = scoreVector.get(p);
            if (score > 0) {
                positiveScore += score;
            } else {
                negtiveScore += score;
            }
        }
        if (positiveScore + negtiveScore < 0) {
            for (let p of scoreVector.keys()) {
                let score = scoreVector.get(p);
                if (score < 0) {
                    score = -(score / negtiveScore) * positiveScore;
                    let roundedScore = parseInt(Math.ceil(score / 10)) * 10;
                    scoreVector.set(p, roundedScore);
                    scoreMatrix.get(p).set(player, -roundedScore);
                }
            }
        }
    }
    let coinResuls = new Map();
    for (let player of scoreMatrix.keys()) {
        let score = 0;
        let scoreVector = scoreMatrix.get(player);
        for (let p of scoreVector.keys()) {
            score += scoreVector.get(p);
        }
        coinResuls.set(player, score);
        logger.info(`tableId=${this.tid}, player=${player.getUid()}, score=${score}`);
    }
    return coinResuls;
};




ZhuojiYuanBaoTable.prototype.playerChuOperation = function (player, card) {
    this.zhuojiGameLogicProcessor.playerChuOperation(this, player, card);
};

ZhuojiYuanBaoTable.prototype.player_op_peng = function (player) {
    this.zhuojiGameLogicProcessor.player_op_peng(this, player);
};

ZhuojiYuanBaoTable.prototype.player_op_gang = function (player, card) {
    this.zhuojiGameLogicProcessor.player_op_gang(this, player, card);
};

ZhuojiYuanBaoTable.prototype.player_op_ting = function (player) {
    this.zhuojiGameLogicProcessor.player_op_ting(this, player);
};

ZhuojiYuanBaoTable.prototype.player_op_hu = function (player) {
    this.zhuojiGameLogicProcessor.player_op_hu(this, player);
};

ZhuojiYuanBaoTable.prototype.player_cancel_chi_peng_gang_hu = function (player) {
    this.zhuojiGameLogicProcessor.player_cancel_chi_peng_gang_hu(this, player);
};





ZhuojiYuanBaoTable.prototype.calculatePlayerGangFans =function(enabledPlayers) {
    let gangScore = new Map();
    for (let player of this.getAllPlayers()) {
        gangScore.set(player, 0);
    }
    for (let player of enabledPlayers) {
        for (let cardDown of player.cardsDown) {
            this.calculateCardDownGangFans(gangScore, player, cardDown);
        }
    }
    return gangScore;
};

ZhuojiYuanBaoTable.prototype.calculateCardDownGangFans = function(gangScore, player, cardDown) {
    if(cardDown.isHanBaoDou === 1){
        return;
    }
    if (cardDown.type === ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG) {
        for (let p of gangScore.keys()) {
            if (p.getUid() !== player.getUid()) {
                gangScore.set(p, gangScore.get(p) - 3);
                gangScore.set(player, gangScore.get(player) + 3);
                this.scoreTuples.push(new ScoreTuple(player, p, 3, ScoreReason.DOU));
            }
        }
    } else if (cardDown.type === ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG) {
        gangScore.set(player, gangScore.get(player) + 3);
        let chuPlayer = this.getPlayerByUid(cardDown.chuPlayerUid);
        gangScore.set(chuPlayer, gangScore.get(chuPlayer) - 3);
        this.scoreTuples.push(new ScoreTuple(player, chuPlayer, 3, ScoreReason.DOU));
    } else if (cardDown.type == ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG) {
        for (let p of gangScore.keys()) {
            if (p.getUid() !== player.getUid()) {
                gangScore.set(p, gangScore.get(p) - 2);
                gangScore.set(player, gangScore.get(player) + 2);
                this.scoreTuples.push(new ScoreTuple(player, p, 2, ScoreReason.DOU));
            }
        }
    }
};

ZhuojiYuanBaoTable.prototype.calculateEnableJiCards = function(players) {
    let playerJiCard = new Map();
    for (let player of players) {
        playerJiCard.set(player, []);
    }
    for (let player of players) {
        let jiCards = this.collectEnableJiCards(player);
        playerJiCard.set(player, jiCards);
    }
    return playerJiCard;
};

ZhuojiYuanBaoTable.prototype.calculateDisableJiCards = function(players) {
    let playerJiCard = new Map();
    for (let player of players) {
        playerJiCard.set(player, []);
    }
    for (let player of players) {
        let jiCards = this.collectDisableJiCards(player);
        playerJiCard.set(player, jiCards);
    }
    return playerJiCard;
};

ZhuojiYuanBaoTable.prototype.collectEnableJiCards = function(player) {
    let jiCards = [];
    if (this.chuifengji && this.jiCard === 0x25) {
        jiCards.push(JiCard.createChuifengJi());
        return jiCards;
    }
    jiCards.push(JiCard.createYaoji());
    if (this.wuguji) {
        jiCards.push(JiCard.createWuGuJi());
    }
    if (this.fanpaiType === 1 && this.jiCard !== 0) {
        jiCards.push(JiCard.createFanPaiJi(this.jiCard));
    }
    if (this.fanpaiType === 2 && this.jiCard !== 0) {
        jiCards.push(...JiCard.createYaobaiJi(this.jiCard));
    }
    if (this.xingqiji) {
        jiCards.push(...JiCard.createWeekJiCard());
    }
    if (this.benji && this.jiCard !== 0) {
        jiCards.push(JiCard.createBenJi(this.jiCard));
    }
    if (this.hasWuguChongfeng(player)) {
        jiCards.push(JiCard.createWuGuChongFeng());
    }
    if (this.hasWuguZeren(player)) {
        jiCards.push(JiCard.createWuGuZeRen());
    }
    if (this.hasYaojiChongfeng(player)) {
        jiCards.push(JiCard.createChongfengJi());
    }
    if (this.hasYaojiZeren(player)) {
        jiCards.push(JiCard.createZeRenJi());
    }
    return jiCards;
};

ZhuojiYuanBaoTable.prototype.collectDisableJiCards = function(player) {
    let jiCards = [];
    if (this.chuifengji && this.jiCard == 0x25) {
        jiCards.push(JiCard.createChuifengJi());
        return jiCards;
    }
    jiCards.push(JiCard.createYaoji());
    if (this.wuguji) {
        jiCards.push(JiCard.createWuGuJi());
    }
    if (this.hasWuguChongfeng(player)) {
        jiCards.push(JiCard.createWuGuChongFeng());
    }
    if (this.hasYaojiChongfeng(player)) {
        jiCards.push(JiCard.createChongfengJi());
    }
    return jiCards;
};

ZhuojiYuanBaoTable.prototype.hasWuguChongfeng = function(player) {
    return player.getPosition() === this.jiPositions.chongFengWugu && this.jiPositions.zeRenWugu === -1;
};

ZhuojiYuanBaoTable.prototype.hasWuguZeren = function(player) {
    return player.getPosition() === this.jiPositions.zeRenWugu;
};

ZhuojiYuanBaoTable.prototype.hasYaojiChongfeng = function(player) {
    return player.getPosition() === this.jiPositions.chongFeng && this.jiPositions.zeRen === -1;
};

ZhuojiYuanBaoTable.prototype.hasYaojiZeren = function(player) {
    return player.getPosition() === this.jiPositions.zeRen;
};

ZhuojiYuanBaoTable.prototype.calculateJiScore = function(enabledPlayers, playerJiCard, finalCard) {
    let jiScores = new Map();
    for (let [player, jiCards] of playerJiCard.entries()) {
        let finalCards = [...player.cardsInHand, ...player.cardsBefore, ...player.cardsDown];
        let gameResult = this.playerGameResults.get(player);
        if (gameResult === ZhuojiGameConstant.PLAYER_RESULT_TYPE_HU) {
            finalCards.push(finalCard);
        }
        let score = this.calculatePlayerJiScore(finalCards, jiCards);
        jiScores.set(player, score);
    }
    let finalJiScores = new Map();
    for (let player of this.getAllPlayers()) {
        finalJiScores.set(player, 0);
    }
    for (let [player, score] of jiScores.entries()) {
        if (score > 0) {
            for (let otherPlayer of this.getAllPlayers()) {
                if (player.getUid() != otherPlayer.getUid()) {
                    finalJiScores.set(player, finalJiScores.get(player) + score);
                    finalJiScores.set(otherPlayer, finalJiScores.get(otherPlayer) - score);
                    this.scoreTuples.push(new ScoreTuple(player, otherPlayer, score, ScoreReason.JI));
                }
            }
        }
    }
    this.calculateZerenScoreForEnable(enabledPlayers, finalJiScores);
    return finalJiScores;
};

ZhuojiYuanBaoTable.prototype.calculateZerenScoreForEnable = function(enabledPlayers, finalJiScores) {
    if(enabledPlayers.length === 0){
        return;
    }
    let jiCards = this.playerJiCards.get(enabledPlayers[0]);
    for(let jiCard of jiCards){
        if(jiCard.type === ZhuojiGameConstant.JI_TYPE_CHUIFENG){
            return;
        }
    }
    if(this.jiPositions.zeRen !== -1){
        let chongFengPlayer = this.getPlayerByTablePos(this.jiPositions.chongFeng)
        let zerenPlayer = this.getPlayerByTablePos(this.jiPositions.zeRen);
        if(containPlayer(enabledPlayers, zerenPlayer)){
            finalJiScores.set(chongFengPlayer, finalJiScores.get(chongFengPlayer) - this.getExtraZerenScoreByPlayType());
            finalJiScores.set(zerenPlayer, finalJiScores.get(zerenPlayer) + this.getExtraZerenScoreByPlayType());
            this.scoreTuples.push(new ScoreTuple(zerenPlayer, chongFengPlayer, this.getExtraZerenScoreByPlayType(), ScoreReason.JI));
        }
    }
    if(this.jiPositions.zeRenWugu !== -1){
        let chongFengWuguPlayer = this.getPlayerByTablePos(this.jiPositions.chongFengWugu);
        let zerenWuguPlayer = this.getPlayerByTablePos(this.jiPositions.zeRenWugu);
        if(containPlayer(enabledPlayers, zerenWuguPlayer)) {
            finalJiScores.set(chongFengWuguPlayer, finalJiScores.get(chongFengWuguPlayer) - this.getExtraZerenWuguScoreByPlayType());
            finalJiScores.set(zerenWuguPlayer, finalJiScores.get(zerenWuguPlayer) + this.getExtraZerenWuguScoreByPlayType());
            this.scoreTuples.push(new ScoreTuple(zerenWuguPlayer, chongFengWuguPlayer, this.getExtraZerenWuguScoreByPlayType(), ScoreReason.JI));
        }
    }
};

ZhuojiYuanBaoTable.prototype.calculatePlayerJiScore = function(finalCards, jiCards) {
    let score = 0;
    for (let jiCard of jiCards) {
        let count = 0;
        for (let c of finalCards) {
            if (c === jiCard.card) {
                count ++;
            }
        }
        jiCard.setCount(count);
        score += jiCard.getScore(this.playType);
    }
    return score;
};


ZhuojiYuanBaoTable.prototype.getExtraZerenScoreByPlayType  = function () {
    return this.playType === ZhuojiGameConstant.PLAY_TYPE_YANG_YANG_SAN ? 2 : 1;
};

ZhuojiYuanBaoTable.prototype.getExtraZerenWuguScoreByPlayType = function() {
    return this.playType === ZhuojiGameConstant.PLAY_TYPE_YANG_YANG_SAN ? 4 : 2;
};

ZhuojiYuanBaoTable.prototype.calculateJiLoseForNotTing = function(disabledPlayers, enabledPlayers, playerGameResults, playerJiCards, playerJiScores) {
    for (let [player, result] of playerGameResults.entries()) {
        if(ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO === result) {
            let finalCards = [...player.cardsBefore, ...player.cardsDown];
            let scoreOfDownAndBeforeJiCard = this.calculatePlayerJiScore(finalCards, playerJiCards.get(player));
            if (scoreOfDownAndBeforeJiCard > 0) {
                for (let [otherPlayer, otherPlayerResult] of playerGameResults.entries()) {
                    if (ZhuojiGameConstant.PLAYER_RESULT_TYPE_CHA_JIAO !== otherPlayerResult) {
                        playerJiScores.set(otherPlayer, playerJiScores.get(otherPlayer) + scoreOfDownAndBeforeJiCard);
                        playerJiScores.set(player, playerJiScores.get(player) - scoreOfDownAndBeforeJiCard);
                        this.scoreTuples.push(new ScoreTuple(otherPlayer, player, scoreOfDownAndBeforeJiCard, ScoreReason.JI));
                    }
                }
            }
        }
    }
    this.calculateZerenScoreForDisable(disabledPlayers, enabledPlayers, playerJiScores);
};

ZhuojiYuanBaoTable.prototype.calculateZerenScoreForDisable = function(disabledPlayers, enabledPlayers, playerJiScores) {
    if(disabledPlayers.length === 0){
        return;
    }
    let jiCards = this.playerJiCards.get(disabledPlayers[0]);
    for(let jiCard of jiCards){
        if(jiCard.type == ZhuojiGameConstant.JI_TYPE_CHUIFENG){
            return;
        }
    }
    if(this.jiPositions.zeRen !== -1){
        let chongFengPlayer = this.getPlayerByTablePos(this.jiPositions.chongFeng);
        let zerenPlayer = this.getPlayerByTablePos(this.jiPositions.zeRen);
        if(containPlayer(disabledPlayers, zerenPlayer) && containPlayer(enabledPlayers, chongFengPlayer)){
            playerJiScores.set(chongFengPlayer, playerJiScores.get(chongFengPlayer) + this.getExtraZerenScoreByPlayType());
            playerJiScores.set(zerenPlayer, playerJiScores.get(zerenPlayer) - this.getExtraZerenScoreByPlayType());
            this.scoreTuples.push(new ScoreTuple(chongFengPlayer, zerenPlayer, this.getExtraZerenScoreByPlayType(), ScoreReason.JI));
        }
    }
    if(this.jiPositions.zeRenWugu != -1){
        let chongFengWuguPlayer = this.getPlayerByTablePos(this.jiPositions.chongFengWugu);
        let zerenWuguPlayer = this.getPlayerByTablePos(this.jiPositions.zeRenWugu);
        if(containPlayer(disabledPlayers, zerenWuguPlayer) && containPlayer(enabledPlayers, chongFengWuguPlayer)){
            playerJiScores.set(chongFengWuguPlayer, playerJiScores.get(chongFengWuguPlayer) + this.getExtraZerenWuguScoreByPlayType());
            playerJiScores.set(zerenWuguPlayer, playerJiScores.get(zerenWuguPlayer) - this.getExtraZerenWuguScoreByPlayType());
            this.scoreTuples.push(new ScoreTuple(chongFengWuguPlayer, zerenWuguPlayer, this.getExtraZerenWuguScoreByPlayType(), ScoreReason.JI));
        }
    }
};

function filterEmptyJiCardWithoutChuifengji(playerJiCards) {
    let newPlayerJiCards = new Map();
    for (let [player, jiCards] of playerJiCards.entries()) {
        let filteredJiCards = [];
        for (let jiCard of jiCards) {
            if (jiCard.count > 0 || jiCard.type === ZhuojiGameConstant.JI_TYPE_CHUIFENG) {
                filteredJiCards.push(jiCard);
            }
        }
        newPlayerJiCards.set(player, filteredJiCards);
    }
    return newPlayerJiCards;
};

function containPlayer(players, player) {
  for(let pl of players) {
      if(player.getUid() === pl.getUid()) {
          return true;
      }
  }
  return false;
};

function calculateTotalScore(huScore, gangScore, jiScore) {
    let totalScore = new Map();
    for (let player of huScore.keys()) {
        let score = huScore.get(player) + gangScore.get(player) + jiScore.get(player);
        totalScore.set(player, score);
    }
    return totalScore;
};







ZhuojiYuanBaoTable.prototype.reset = function () {
    this.resultType = 0;
    this.currentPosition = -1;  //当前操作玩家
    this.chu = {uid: -1, position: -1, chuCard: 0};
    // this.cardOpPlayerPosition = -1;         //出牌的玩家，可能会引发别的玩家的碰杠胡
    // this.currentCard = 0;
    // this.chuPlayer = null;
    this.actorUid = -1;
    this.currentHuPlayer = null;
    this.gangOpBackup = null;       //抢杠的数据备份
    this.lastAction = 0;            //上一次操作
    this.dealerUid = 0;
    this.cards = [];                //底牌
    this.cardPool = new Map();      //牌池
    this.waitingPlayerOperate = null;
    this.readyTime = 0;             //所有玩家都准备的时间
    this.handStartTime = 0;         //牌局开始时间
    this.handEndTime = 0;           //牌局结束时间
    this.lastEnterTime = 0;

    this.jiCard = 0;                //牌局结束后所翻的鸡牌
    this.jiPositions = {chongFeng: -1, zeRen: -1, chongFengWugu: -1, zeRenWugu: -1};

    this.playingFlag = 0;
    this.readyFlag = 0;
    this.players.forEach(function (player) {
        if (player != null) {
            player.resetInTable();
            player.clearReady();
            player.startGetReadyCountDown(16);
        }
    });
};


ZhuojiYuanBaoTable.prototype.tick = function () {
    // logger.info(`ZhuojiYuanBaoTable tick ${this.state}`);
    // if (this.state === ZhuojiGameConstant.TABLE_STATE_WAITING_PLAYER) {
    if (this.playingFlag === 0) {
        this.zhuojiGameLogicProcessor.processWatingPlayerState(this);
    }
    // 游戏正在进行中
    // else if (this.state === ZhuojiGameConstant.TABLE_STATE_PLAYING) {
    else if (this.playingFlag === 1) {
        this.zhuojiGameLogicProcessor.playingTableTick(this);
    }
};

ZhuojiYuanBaoTable.prototype.toYuanBaoTable= function () {
    return {

    };
};

ZhuojiYuanBaoTable.prototype.toJSON = function () {
    let playersJSON = [];
    this.players.forEach(function (player) {
        if (player != null) {
            playersJSON.push(player.toJSON());
        }
    });
    return {
        tid: this.tid,
        code: this.code,
        multiplier: this.multiplier,
        maxPlayers: this.maxPlayers,
        creatorUid: this.creatorUid,
        playingFlag: this.playingFlag,
        actorUid: this.actorUid,
        dealerUid: this.dealerUid,
        jiTypes: [this.fanpaiType, this.benji ? 1 : 2, this.wuguji ? 1 : 2, this.xingqiji ? 1 : 2, this.chuifengji ? 1 : 2, this.playType ? 1 : 2],
        playType: this.playType,
        jiPositions: [this.jiPositions.chongFeng, this.jiPositions.zeRen, this.jiPositions.chongFengWugu, this.jiPositions.zeRenWugu],
        leftNum: this.getCardLeftNum(),
        players: playersJSON
    };
};

module.exports.create = function (tableCfg) {
    let table = new ZhuojiYuanBaoTable();
    table.tid = tableCfg['tid'];
    table.code = tableCfg['code'];
    table.room = tableCfg['room'];
    table.multiplier = table.room.multiplier;
    table.visible = tableCfg['visible'];
    table.creatorUid = tableCfg['creatorUid'];
    table.maxPlayers = tableCfg["maxPlayers"];
    table.createTime = DateUtil.nowMS();
    for (let i = 0; i < table.maxPlayers; i++) {
        table.players.push(null);
    }
    table.gid = tableCfg['gid'];
    table.zhuojiGameLogicProcessor = new ZhuojiGameLogicProcessor();
    return table;
};
